package com.client;

import java.util.*;

import com.google.gwt.http.client.URL;
import com.google.gwt.user.client.ui.InlineLabel;
import com.google.gwt.visualization.client.DataTable;
import com.google.gwt.visualization.client.Query;
import com.google.gwt.visualization.client.QueryResponse;
import com.google.gwt.visualization.client.Query.Callback;

public class PagedTagsPanel extends TagsPanel {
	private HashMap<String, Tags> tagTable;
	private String curr = "";
	private int pageSize = 10;

	public PagedTagsPanel(int pageSize) {
		super(pageSize, 1, new ClkAddTag());
		this.pageSize = pageSize;
	}

	protected void onAttach() {
		this.tagTable = new HashMap<String, Tags>();
		this.insert(new InlineLabel(CNStatsFront.get().constants.relatedTags()), 0);
		super.onAttach();
	}

	public void showTags(String related, String direction) {
		CNStatsFront.get().plSearch.btnPrev.setEnabled(false);
		CNStatsFront.get().plSearch.btnNext.setEnabled(false);
		this.clearTags();
		CNStatsFront.get().showLoading(true);
		if (related.equals(""))
			related = "__NULL__";
		if (false == this.tagTable.containsKey(related)) {
			this.tagTable.put(related, new Tags(pageSize));
		}
		this.setCurr(related);
		ArrayList<String> tags = null;
		if (direction.equals("next")) {
			tags = this.tagTable.get(related).next();
			if (null == tags && true == this.tagTable.get(related).hasMore()) {
				String searchURL;
				if (related.equals("__NULL__")) {
					searchURL = CNStatsFront.baseURL
							+ "querytags?direction=next&pagesize=" + this.pageSize;
					if (false == this.tagTable.get(related).getBookmark().equals(""))
						searchURL = searchURL + "&tag="
								+ this.tagTable.get(related).getBookmark();
				} else {
					searchURL = CNStatsFront.baseURL + "querytags?direction=related&tag="
							+ related + "&pagesize=" + this.pageSize;
				}
				Query.create(URL.encode(searchURL)).send(new CBQueryTags(related));
				return;
			}
		} else if (direction.equals("prev")) {
			tags = this.tagTable.get(related).prev();
		}
		CNStatsFront.get().showLoading(false);
		if (null == tags) {
			CNStatsFront.get().showInfo(CNStatsFront.get().constants.noMoreTags());
			return;
		}
		for (Iterator<String> iter = tags.iterator(); iter.hasNext();) {
			this.addTag(iter.next());
		}
		CNStatsFront.get().plSearch.btnPrev.setEnabled(
				this.tagTable.get(related).hasPrev());
		CNStatsFront.get().plSearch.btnNext.setEnabled(
				this.tagTable.get(related).hasNext());
	}

	public void addTag(String related, String tag, Boolean isBookmark) {
		if (related.equals(""))
			related = "__NULL__";
		if (false == this.tagTable.containsKey(related)) {
			this.tagTable.put(related, new Tags(pageSize));
		}
		this.tagTable.get(related).add(tag);
		if (true == isBookmark)
			this.tagTable.get(related).setBookmark(tag);
	}

	public void setCurr(String curr) {
		if (false == this.curr.equals(curr)
				&& true == this.tagTable.containsKey(this.curr)) {
			this.tagTable.get(this.curr).reset();
		}
		this.curr = curr;
	}

	public void next() {
		assert (this.tagTable.containsKey(this.curr));
		this.showTags(this.curr, "next");
	}

	public void prev() {
		this.showTags(this.curr, "prev");
	}

	public Tags getCurrTags() {
		if (false == this.tagTable.containsKey(this.curr)) {
			this.tagTable.put(this.curr, new Tags(pageSize));
		}
		return this.tagTable.get(this.curr);
	}

	public void reset() {
		this.setCurr("__NULL__");
		this.getCurrTags().reset();
		this.showTags("", "next");
	}
}

final class CBQueryTags implements Callback {
	private String related;

	public CBQueryTags(String related) {
		this.related = related;
	}

	public void onResponse(QueryResponse response) {
		CNStatsFront.get().showLoading(false);
		if (response.isError()) {
			CNStatsFront.get().showInfo(
					response.getMessage() + ":" + response.getDetailedMessage());
			return;
		} else {
			// Get the data from the QueryResponse.
			DataTable data = response.getDataTable();

			if (0 == data.getNumberOfRows()) {
				CNStatsFront.get().showInfo(CNStatsFront.get().constants.noMoreTags());
				CNStatsFront.get().plSearch.ptpTags.getCurrTags().setNoResult(true);
				return;
			}
			for (int i = 0; i < data.getNumberOfRows(); i++) {
				CNStatsFront.get().plSearch.ptpTags
						.addTag(this.related, data.getValueString(i, 0), data
								.getValueString(i, 2).equals("bookmark"));
			}
			CNStatsFront.get().plSearch.ptpTags.showTags(this.related, "next");
		}
	}
}

class Tags extends Object {
	private ArrayList<ArrayList<String>> tags;
	private int curr;
	private int pageSize;
	private String bookmark = "";
	private Boolean noResult = false;

	public Tags(int pageSize) {
		this.tags = new ArrayList<ArrayList<String>>();
		this.curr = -1;
		this.pageSize = pageSize;
	}

	public boolean hasNext() {
		return (false == this.tags.isEmpty() && this.curr < (this.tags.size() - 1))
				|| (false == this.tags.isEmpty() && this.curr == (this.tags.size() - 1)
						&& false == this.bookmark.equals("") && this.pageSize == this.tags
						.get(this.tags.size() - 1).size());
	}

	public boolean hasPrev() {
		return false == this.tags.isEmpty() && this.curr > 0;
	}

	public boolean hasMore() {
		return false == this.noResult
				&& (true == this.tags.isEmpty() || this.pageSize == this.tags.get(
						this.tags.size() - 1).size());
	}

	public void add(String tag) {
		if (true == this.tags.isEmpty()
				|| this.pageSize == this.tags.get(this.tags.size() - 1).size())
			this.tags.add(new ArrayList<String>());
		ArrayList<String> lastPage = this.tags.get(this.tags.size() - 1);
		lastPage.add(tag);
	}

	public ArrayList<String> next() {
		if (true == this.tags.isEmpty() || this.tags.size() - 1 == curr) {
			return null;
		}

		this.curr++;
		return this.tags.get(this.curr);

	}

	public ArrayList<String> prev() {
		if (0 == this.curr)
			return null;
		this.curr--;
		return this.tags.get(this.curr);
	}

	public void reset() {
		this.curr = -1;
	}

	public void setBookmark(String bookmark) {
		this.bookmark = bookmark;
	}

	public String getBookmark() {
		return bookmark;
	}

	public void setNoResult(Boolean noResult) {
		this.noResult = noResult;
	}
}
